package top.fangwenmagician.calculationstring.operator;

import top.fangwenmagician.calculationstring.ProcessControl;
import top.fangwenmagician.calculationstring.exception.CalculationException;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * <h1>命名正则运算符抽象类</h1>
 * 该类提供了一种简单的{@link I_Operator}实现<br />
 * 可以通过继承该抽象类，实现一个基于 命名正则捕获组 的运算符
 */
public abstract class A_NamedRegularOperator extends A_RegularOperator {
    /**
     * 命名捕获组所使用的命名列表
     *
     * @return 命名列表
     */
    public abstract String[] NamedList() throws Exception;

    /**
     * 用于命名捕获组所使用的命名列表的工具函数。<br />
     * 不推荐使用，在准确度上没法保证，在性能上也相对较慢。
     *
     * @return 命名列表
     * @throws Exception 可能出现的异常
     */
    @Deprecated
    public String[] defaultNamedList() throws Exception {
        Pattern pattern = Pattern.compile("\\?<(\\w*)>");
        Matcher matcher = pattern.matcher(this.regularText());
        List<String> stringList = new ArrayList<>();
        while (matcher.find()) {
            stringList.add(matcher.group(1));
        }
        return stringList.toArray(new String[0]);
    }

    /**
     * 使用命名捕获组时触发的的计算函数
     *
     * @param captureStringMap 被捕获的对象map
     * @param processControl   流程控制器，可用于回调计算或获取自定义配置
     * @return 运算的结果
     * @throws Exception 计算中抛出的异常
     */
    public abstract String calculation(Map<String, String> captureStringMap, ProcessControl processControl) throws Exception;

    @Override
    public String calculation(String expression, ProcessControl processControl) throws Exception {
        Pattern pattern = Pattern.compile(this.regularText());
        Matcher matcher = pattern.matcher(expression);
        if (matcher.find()) {
            Map<String, String> map = new HashMap<>();
            for (String name : this.NamedList()) {
                map.put(name, matcher.group(name));
            }
            map.put("", matcher.group(0));
            String calculationResults = matcher.replaceFirst(this.calculation(map, processControl));
            processControl.writeLog(calculationResults);
            return calculationResults;
        } else {
            throw new CalculationException("未捕获到字符串");
        }
    }
}
